﻿using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Wordprocessing;
using DocumentFormat.OpenXml;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace OpenXmlTools.OpenXML.Word
{
    /// <summary>
    /// Word书签帮助类
    /// </summary>
    public static class WordBookmarkHelper
    {
        /// <summary>
        /// 根据模板生成新文件并替换新文件中书签标记的内容
        /// </summary>
        /// <param name="fileStream">模板文件的文件流</param>
        /// <param name="replacements">需要替换的书签键值对集合（键：书签名称；值：需要替换成的文本）</param>
        /// <returns>新文件文件流</returns>
        public static MemoryStream ReplaceDocumentBookmarks(Stream fileStream, Dictionary<string, string> replacements)
        {
            try
            {
                // 创建文档的可写副本
                using var memoryStream = new MemoryStream();
                fileStream.CopyTo(memoryStream);
                memoryStream.Position = 0;
                fileStream.Position = 0;

                // 打开Word文档
                using (WordprocessingDocument doc = WordprocessingDocument.Open(memoryStream, true))
                {
                    ReplaceDocumentBookmarks(doc, replacements);
                }

                // 重置流的读取/写入位置到起始点
                memoryStream.Position = 0;

                return memoryStream;
            }
            catch (Exception)
            {

                throw;
            }
        }

        /// <summary>
        /// 替换Word文档中书签标记的内容
        /// </summary>
        /// <param name="doc">Word文档</param>
        /// <param name="replacements">需要替换的书签键值对集合（键：书签名称；值：需要替换成的文本）</param>
        public static void ReplaceDocumentBookmarks(WordprocessingDocument doc, Dictionary<string, string> replacements)
        {
            try
            {
                var mainPart = doc.MainDocumentPart;
                if (mainPart != null)
                {
                    var bookmarkStarts = mainPart.Document.Descendants<BookmarkStart>().ToList();

                    foreach (var bookmarkStart in bookmarkStarts)
                    {
                        if (bookmarkStart != null)
                        {
                            var bookmarkName = bookmarkStart.Name;
                            if (!string.IsNullOrWhiteSpace(bookmarkName))
                            {
                                if (replacements.TryGetValue(bookmarkName, out string replacementText))
                                {
                                    BookmarkEnd? bookmarkEnd = mainPart.Document.Descendants<BookmarkEnd>().FirstOrDefault(b => b.Id?.Value == bookmarkStart.Id);

                                    if (bookmarkEnd != null)
                                    {
                                        // 获取书签原始位置保持格式
                                        RunProperties preservedFormatting = GetPreservedFormatting(bookmarkStart);

                                        // 替换书签内容
                                        ReplaceBookmarkContent(bookmarkStart, bookmarkEnd, replacementText, preservedFormatting);
                                    }
                                }
                            }
                        }
                    }

                    // 保存文档
                    mainPart.Document.Save();
                }
            }
            catch (Exception)
            {

                throw;
            }
        }

        /// <summary>
        /// 获取书签原始位置的格式
        /// </summary>
        /// <param name="bookmarkStart"></param>
        /// <returns></returns>
        private static RunProperties GetPreservedFormatting(BookmarkStart bookmarkStart)
        {
            // 尝试从书签后面的第一个Run获取格式
            var nextSibling = bookmarkStart.NextSibling();
            while (nextSibling != null && !(nextSibling is Run))
            {
                nextSibling = nextSibling.NextSibling();
            }

            if (nextSibling is Run originalRun && originalRun.RunProperties != null)
            {
                return (RunProperties)originalRun.RunProperties.CloneNode(true);
            }

            // 尝试从书签前面的Run获取格式
            var prevSibling = bookmarkStart.PreviousSibling();
            while (prevSibling != null && !(prevSibling is Run))
            {
                prevSibling = prevSibling.PreviousSibling();
            }

            if (prevSibling is Run previousRun && previousRun.RunProperties != null)
            {
                return (RunProperties)previousRun.RunProperties.CloneNode(true);
            }

            // 返回默认格式（无特殊格式）
            return new RunProperties();
        }

        /// <summary>
        /// 替换书签的内容
        /// </summary>
        /// <param name="start"></param>
        /// <param name="end"></param>
        /// <param name="text"></param>
        /// <param name="preservedFormatting"></param>
        private static void ReplaceBookmarkContent(BookmarkStart start, BookmarkEnd end, string text, RunProperties preservedFormatting)
        {
            if (start != null && end != null)
            {
                // 清理书签之间的内容
                CleanContentBetweenBookmarks(start, end);

                // 创建新的带格式的Run
                var newRun = new Run(preservedFormatting.CloneNode(true));
                newRun.AppendChild(new Text(text));

                // 将新内容插入书签起始位置后面
                start.Parent?.InsertAfter(newRun, start);
            }
        }

        /// <summary>
        /// 清空书签之间原有的内容
        /// </summary>
        /// <param name="start"></param>
        /// <param name="end"></param>
        private static void CleanContentBetweenBookmarks(BookmarkStart start, BookmarkEnd end)
        {
            if (start != null && end != null)
            {
                var elementsToRemove = new List<OpenXmlElement>();
                OpenXmlElement? current = start.NextSibling();

                //while (current != null && !(current is BookmarkEnd bookmarkEnd && (current as BookmarkEnd).Id == start.Id))
                while (current != null && current != end)
                {
                    elementsToRemove.Add(current);
                    current = current.NextSibling();
                }

                foreach (var element in elementsToRemove)
                {
                    element.Remove();
                }
            }
        }

    }
}
